package com.hys.app.service.erp.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hys.app.converter.erp.StockTransferConverter;
import com.hys.app.converter.erp.StockTransferProductConverter;
import com.hys.app.framework.context.user.AdminUserContext;
import com.hys.app.framework.database.WebPage;
import com.hys.app.framework.exception.ServiceException;
import com.hys.app.framework.database.mybatisplus.base.BaseServiceImpl;
import com.hys.app.framework.rabbitmq.MessageSender;
import com.hys.app.framework.rabbitmq.MqMessage;
import com.hys.app.framework.util.DateUtil;
import com.hys.app.framework.util.PageConvert;
import com.hys.app.mapper.erp.StockTransferMapper;
import com.hys.app.model.base.rabbitmq.AmqpExchange;
import com.hys.app.model.erp.dos.StockTransferDO;
import com.hys.app.model.erp.dos.StockTransferProductDO;
import com.hys.app.model.erp.dos.WarehouseDO;
import com.hys.app.model.erp.dos.WarehouseEntryBatchDO;
import com.hys.app.model.erp.dto.*;
import com.hys.app.model.erp.enums.*;
import com.hys.app.model.erp.vo.StockTransferAllowable;
import com.hys.app.model.erp.vo.StockTransferStatistics;
import com.hys.app.model.erp.vo.StockTransferVO;
import com.hys.app.model.system.dos.AdminUser;
import com.hys.app.model.system.dos.DeptDO;
import com.hys.app.model.system.dto.DataPermissionDTO;
import com.hys.app.service.erp.*;
import com.hys.app.service.system.AdminUserManager;
import com.hys.app.service.system.DeptManager;
import com.hys.app.service.system.RoleManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;

import static com.hys.app.framework.util.CollectionUtils.convertList;

/**
 * 库存调拨业务层实现
 *
 * @author 张崧
 * @since 2023-12-12 11:59:06
 */
@Service
public class StockTransferManagerImpl extends BaseServiceImpl<StockTransferMapper, StockTransferDO> implements StockTransferManager {

    @Autowired
    private StockTransferConverter converter;

    @Autowired
    private StockTransferProductConverter itemConverter;

    @Autowired
    private WarehouseEntryBatchManager warehouseEntryBatchManager;

    @Autowired
    private WarehouseManager warehouseManager;

    @Autowired
    private NoGenerateManager noGenerateManager;

    @Autowired
    private StockTransferProductManager stockTransferProductManager;

    @Autowired
    private ProductStockManager productStockManager;

    @Autowired
    private DeptManager deptManager;

    @Autowired
    private StockTransferMapper stockTransferMapper;

    @Autowired
    private MessageSender messageSender;

    @Autowired
    private RoleManager roleManager;

    @Autowired
    private AdminUserManager adminUserManager;

    @Autowired
    private WarehouseEntryBatchManager batchManager;

    @Override
    public WebPage<StockTransferVO> list(StockTransferQueryParams queryParams) {
        // 按部门做数据过滤
        DataPermissionDTO dataPermission = roleManager.getDataPermission(AdminUserContext.getAdminUserId());
        if(!dataPermission.getAll()){
            queryParams.setDeptIdList(dataPermission.getDeptIds());
        }

        WebPage<StockTransferDO> webPage = baseMapper.selectPage(queryParams);

        return converter.combination(webPage, dataPermission);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(StockTransferDTO stockTransferDTO) {
        // 校验传参
        check(stockTransferDTO);

        // 保存调拨单
        StockTransferDO transferDO = converter.combination(stockTransferDTO);
        transferDO.setSn(noGenerateManager.generate(NoBusinessTypeEnum.StockTransfer, transferDO.getOutDeptId()));
        transferDO.setStatus(StockTransferStatusEnum.NotSubmit);
        save(transferDO);

        // 保存调拨单明细
        List<StockTransferProductDO> productList = itemConverter.combination(stockTransferDTO.getProductList(), stockTransferDTO.getBatchMap(), transferDO.getId());
        stockTransferProductManager.saveBatch(productList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void edit(StockTransferDTO stockTransferDTO) {
        StockTransferDO oldDO = getById(stockTransferDTO.getId());
        if (!new StockTransferAllowable(oldDO).getEdit()) {
            throw new ServiceException("当前状态不允许编辑");
        }

        // 校验传参
        check(stockTransferDTO);

        // 保存调拨单
        StockTransferDO transferDO = converter.combination(stockTransferDTO);
        updateById(transferDO);

        // 保存调拨单明细
        List<StockTransferProductDO> productList = itemConverter.combination(stockTransferDTO.getProductList(), stockTransferDTO.getBatchMap(), transferDO.getId());
        stockTransferProductManager.deleteByStockTransferId(Collections.singletonList(stockTransferDTO.getId()));
        stockTransferProductManager.saveBatch(productList);
    }

    @Override
    public StockTransferVO getDetail(Long id) {
        StockTransferVO stockTransferVO = converter.convert(getById(id));
        List<StockTransferProductDO> productList = stockTransferProductManager.listByStockTransferId(id);
        stockTransferVO.setProductList(itemConverter.convert(productList));
        return stockTransferVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(List<Long> ids) {
        List<StockTransferDO> stockTransferList = listByIds(ids);
        for (StockTransferDO stockTransferDO : stockTransferList) {
            if (!new StockTransferAllowable(stockTransferDO).getDelete()) {
                throw new ServiceException(stockTransferDO.getSn() + "不能进行删除操作");
            }
        }
        removeBatchByIds(ids);
        stockTransferProductManager.deleteByStockTransferId(ids);
    }

    @Override
    public void submit(Long id) {
        // 校验
        StockTransferDO oldDO = getById(id);
        if (!new StockTransferAllowable(oldDO).getSubmit()) {
            throw new ServiceException("当前状态不允许提交");
        }

        lambdaUpdate()
                .set(StockTransferDO::getStatus, StockTransferStatusEnum.Submit)
                .eq(StockTransferDO::getId, id)
                .update();
    }

    @Override
    public void withdraw(Long id) {
        // 校验
        StockTransferDO oldDO = getById(id);
        if (!new StockTransferAllowable(oldDO).getWithdraw()) {
            throw new ServiceException("当前状态不允许撤销");
        }

        lambdaUpdate()
                .set(StockTransferDO::getStatus, StockTransferStatusEnum.NotSubmit)
                .eq(StockTransferDO::getId, id)
                .update();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void confirm(List<Long> ids, Long handleById) {
        List<StockTransferDO> stockTransferList = listByIds(ids);
        for (StockTransferDO stockTransferDO : stockTransferList) {
            if (!new StockTransferAllowable(stockTransferDO).getConfirm()) {
                throw new ServiceException(stockTransferDO.getSn() + "不能进行确认操作");
            }
        }

        AdminUser adminUser = adminUserManager.getModel(handleById);
        if(adminUser == null){
            throw new ServiceException("经手人不存在");
        }

        // 更新调拨单状态
        lambdaUpdate()
                .set(StockTransferDO::getStatus, StockTransferStatusEnum.Confirm)
                .set(StockTransferDO::getInHandledById, handleById)
                .set(StockTransferDO::getInHandledByName, adminUser.getRealName())
                .in(StockTransferDO::getId, ids)
                .update();

        for (StockTransferDO stockTransferDO : stockTransferList) {
            List<StockTransferProductDO> transferProductList = stockTransferProductManager.listByStockTransferId(stockTransferDO.getId());
            // 减少批次的库存
            List<StockUpdateDTO> stockReduceList = converter.convertStockUpdateList(transferProductList);
            batchManager.updateStock(StockChangeSourceEnum.STOCK_TRANSFER, stockTransferDO.getSn(), stockReduceList);

            // 创建调入仓库的入库批次
            List<WarehouseEntryBatchDO> batchList = converter.combination(stockTransferDO, transferProductList, noGenerateManager);
            batchManager.create(StockChangeSourceEnum.STOCK_TRANSFER, stockTransferDO.getSn(), batchList);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void reject(List<Long> ids, Long handleById) {
        List<StockTransferDO> stockTransferList = listByIds(ids);
        for (StockTransferDO stockTransferDO : stockTransferList) {
            if (!new StockTransferAllowable(stockTransferDO).getConfirm()) {
                throw new ServiceException(stockTransferDO.getSn() + "不能进行退回操作");
            }
        }

        AdminUser adminUser = adminUserManager.getModel(handleById);
        if(adminUser == null){
            throw new ServiceException("经手人不存在");
        }

        // 更新调拨单状态
        lambdaUpdate()
                .set(StockTransferDO::getStatus, StockTransferStatusEnum.NotSubmit)
                .set(StockTransferDO::getInHandledById, handleById)
                .set(StockTransferDO::getInHandledByName, adminUser.getRealName())
                .in(StockTransferDO::getId, ids)
                .update();
    }

    private void check(StockTransferDTO stockTransferDTO) {
        DeptDO outDeptDO = deptManager.getById(stockTransferDTO.getOutDeptId());
        if (outDeptDO == null) {
            throw new ServiceException("调出方部门不存在");
        }

        DeptDO inDeptDO = deptManager.getById(stockTransferDTO.getInDeptId());
        if (inDeptDO == null) {
            throw new ServiceException("调入方部门不存在");
        }

        WarehouseDO outWarehouseDO = warehouseManager.getById(stockTransferDTO.getOutWarehouseId());
        if (!outWarehouseDO.getDeptId().equals(stockTransferDTO.getOutDeptId())) {
            throw new ServiceException("调出方部门不存在该仓库");
        }
        WarehouseDO inWarehouseDO = warehouseManager.getById(stockTransferDTO.getInWarehouseId());
        if (!inWarehouseDO.getDeptId().equals(stockTransferDTO.getInDeptId())) {
            throw new ServiceException("调入方部门不存在该仓库");
        }

        AdminUser outHandledBy = adminUserManager.getModel(stockTransferDTO.getOutHandledById());
        if (outHandledBy == null) {
            throw new ServiceException("调出方经手人不存在");
        }
        AdminUser inHandledBy = null;
        if(stockTransferDTO.getInHandledById() != null){
            inHandledBy = adminUserManager.getModel(stockTransferDTO.getInHandledById());
            if (inHandledBy == null) {
                throw new ServiceException("调入方经手人不存在");
            }
        }


        // 校验入库批次
        List<Long> batchIds = convertList(stockTransferDTO.getProductList(), StockTransferProductDTO::getWarehouseEntryBatchId);
        Map<Long, WarehouseEntryBatchDO> batchMap = warehouseEntryBatchManager.listAndConvertMap(batchIds, WarehouseEntryBatchDO::getId);
        for (StockTransferProductDTO transferProductDTO : stockTransferDTO.getProductList()) {
            WarehouseEntryBatchDO batchDO = batchMap.get(transferProductDTO.getWarehouseEntryBatchId());
            if (batchDO == null) {
                throw new ServiceException("批次：" + transferProductDTO.getWarehouseEntryBatchId() + "不存在");
            }
            if (!batchDO.getWarehouseId().equals(stockTransferDTO.getOutWarehouseId())) {
                throw new ServiceException("批次：" + batchDO.getSn() + "和调出方仓库不一致");
            }
            if (batchDO.getRemainNum() < transferProductDTO.getNum()) {
                throw new ServiceException("批次：" + batchDO.getSn() + "库存不足");
            }
        }

        // 填充批次数据
        stockTransferDTO.setBatchMap(batchMap);
        stockTransferDTO.setOutDeptDO(outDeptDO);
        stockTransferDTO.setInDeptDO(inDeptDO);
        stockTransferDTO.setOutWarehouseDO(outWarehouseDO);
        stockTransferDTO.setInWarehouseDO(inWarehouseDO);
        stockTransferDTO.setOutHandleBy(outHandledBy);
        stockTransferDTO.setInHandleBy(inHandledBy);
    }

    /**
     * 查询库存调拨统计分页列表数据
     *
     * @param params 查询参数
     * @return
     */
    @Override
    public WebPage statistics(StockTransferStatisticsParam params) {
        IPage<StockTransferStatistics> iPage = this.stockTransferMapper.selectStockTransferPage(new Page(params.getPageNo(), params.getPageSize()), params);
        return PageConvert.convert(iPage);
    }

    /**
     * 导出库存调拨统计列表
     *
     * @param response
     * @param params   查询参数
     */
    @Override
    public void export(HttpServletResponse response, StockTransferStatisticsParam params) {
        //查询库存调拨商品列表
        List<StockTransferStatistics> list = this.stockTransferMapper.selectStockTransferList(params);

        ArrayList<Map<String, Object>> rows = CollUtil.newArrayList();
        ExcelWriter writer = ExcelUtil.getWriter(true);
        for (StockTransferStatistics statistics : list) {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("调拨单号", statistics.getSn());
            map.put("调拨时间", DateUtil.toString(statistics.getTransferTime(), "yyyy-MM-dd HH:mm:ss"));
            map.put("入库单号", statistics.getWarehouseEntrySn());
            map.put("商品编号", statistics.getProductSn());
            map.put("商品名称", statistics.getProductName());
            map.put("商品类别", statistics.getCategoryName());
            map.put("规格型号", statistics.getProductSpecification());
            map.put("单位", statistics.getProductUnit());
            map.put("调入仓库", statistics.getInWarehouseName());
            map.put("调出仓库", statistics.getOutWarehouseName());
            map.put("调拨数量", statistics.getNum());
            rows.add(map);
        }

        writer.write(rows, true);

        ServletOutputStream out = null;
        try {
            out = response.getOutputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
        response.setHeader("Content-Disposition", "attachment;filename=stock_transfer.xlsx");
        writer.flush(out, true);
        writer.close();
        IoUtil.close(out);
    }

    @Override
    public long countByWarehouseId(Long warehouseId) {
        return lambdaQuery()
                .eq(StockTransferDO::getInWarehouseId, warehouseId)
                .or()
                .eq(StockTransferDO::getOutWarehouseId, warehouseId)
                .count();
    }
}

